スマホのNFCタッチで昼休みのSlack報告を楽にしてみた

スマホのNFCタッチで昼休みのSlack報告を楽にしてみた

Clock Icon2023.05.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部のueharaです。

今回は、NFCとMQTTを使って昼休みのSlack報告を楽にしてみたいと思います。

はじめに

私が所属するデータアナリティクス事業本部では、お昼休みにはいる前にSlackで宣言してから休憩に入ることになっています。

最近お昼休みの投稿に関する自動化についてブログ記事がいくつか出ていることもあり、私も便乗して自動化したくなりました。

自動化案

私が極度の面倒くさがりであることもあり、できればPCの操作すらせずに対応したいです。

何か自身の習慣に紛れ込ますことができないかと考えたとき、私はお昼ご飯のためデスクから立ち上がり移動する際に、一緒にスマホを持っていくことに気づきました。

そのため、デスクから離れる際スマホで何かにさっとかざすだけでSlackに投稿できたら楽なんじゃ?と思い、以下システムを考えてみました。

MQTTとは

MQTTはMessage Queueing Telemetry Transportの略で、TCP/IPネットワークで利用できるシンプルで軽量な通信プロトコルです。

MQTTはPublish/Subscribe型のモデルを採用しており、AWSでいうとSNSを想像するとイメージがつきやすいと思います。

NFCとは

NFCはNear Field Communicationの略で、近距離無線通信のことです。

皆さんが電車で使っているSuicaもFeliCaというNFC Type-F(ISO/IEC 18092)になります。

国内で販売されているスマートフォンの多くは、FeliCa(NFC Type-F)ないしクレカのタッチ決済で使われているようなNFC Type-A/Bが搭載されています。

上記を組み合わせると、先程の絵で示したように「スマホでかざすだけでSlack投稿を自動化する」ことが可能です。

MQTTブローカーの設定

先程MQTTはPublish/Subscribe型を採用していると言いましたが、これには「MQTTブローカー」という仲介役の役割を果たすシステムが必要になります。先程の絵でも真ん中に鎮座しています。

今回はMQTTブローカーとして簡単に利用することができるパブリッククラウドのサービスであるBeebotteを使用したいと思います。

Beebotteの料金プランは以下のようになっており、無料枠でも1日に50,000ものメッセージを送信することができます。今回のユースケースでは確実に無料のまま利用できます。

Beebotteの設定

サインアップを済ませると、以下のような画面がトップページに表示されるため、右上のボタンからチャンネルを作成していきます。

私はチャンネル名をlunchに設定し、リソースにnotifyを作成しました。これにより、lunch/notifyというトピックに対しPub/Subができるようになります。

最後にCreate Channelボタンを押せばチャンネルの作成が完了です。非常に簡単ですね!

作成したチャンネルに移動すると、以下のようにTokenが確認できます。接続に必要な情報になるのでメモしておきましょう。

NFCとスマホ側のアプリ設定

それでは、NFCとスマホアプリの設定を行いたいと思います。

私はAndroidを利用していますが、今回はMQTT NFC Readerというアプリを利用したいと思います。

アプリの機能をざっくり説明するとNFCタグに書き込みができ、そのタグを読み込むとMQTTで任意のチャンネルに任意の値をPubすることができるというものです。

今回はNFCタグを用意し、そちらをスマホで読み込むとMQTTでメッセージを送信するようにしたいと思います。

NFCタグの用意

NFCタグとして、NTAG215のコイン型のタグを用意しました。

Amazonであれば30枚入りでも1,000円以下で売っています。そんなにいらないよ!という方には、フリマサイトでは300円/3つでバラ売りされていたりもするので、そちらで購入するのが良いと思います。

MQTT NFC Readerの設定

アプリを立ち上げるとHost名等を入力する画面が表示されるので、以下で設定します。

  • Hostname: mqtt.beebotte.com
  • Port: 1883
  • Username: token:token_(Beebotteのトークン)
  • Password: 設定なし

次にNFCタグに書き込むデータを入力する画面が表示されるので、以下を設定します。

  • Topic: lunch/notify
  • Value: lunch_notification

上記の入力が完了すると、アプリを閉じてNFCタグをスマホにかざすよう促されるので、言われた通り実施を行います。

NFCタグをかざした後、緑丸にチェックマークがついた画面が表示されたら書き込み成功です。

書き込まれたデータの確認

中に何が書き込まれたか気になるので、中身を見てみます。

AndroidでNFCの情報を読み込むアプリはNFC TagInfo by NXPが表示が分かりやすくておすすめです。

NFC TagInfo by NXPを起動して「FULL SCAN」を選択したあと、先程書き込みを行ったNFCタグをかざし中身を確認すると、以下のようになっておりました。

データを見る限り、きちんと書き込みできていそうです!

Subするプログラムの設定

Pythonで先に設定したBeebotteのlunch/notifyトピックをSubし、lunch_notificationの通知があったらSlackに投稿するプログラムを書きます。

事前準備

Slack投稿用のTOKENやUSER_IDの取得方法については、冒頭で紹介したこちらのブログ記事をご参照ください。

また、mqttを扱うPythonモジュールにpaho-mqttがあるので、事前にインストールしておきます。

pip install paho-mqtt

スクリプト

以下のように、1ファイルにざっと書いてみました。

from threading import Lock

import paho.mqtt.client as mqtt
import requests

# beebotte関連定数定義
HOST = "mqtt.beebotte.com"
PORT = 1883
TOPIC = "lunch/notify"
BEEBOTTE_ACCESS_TOKEN = "token:token_<YOUR_BEEBOTTE_TOKEN>"
# slack関連定数定義
SLACK_TOKEN = "<YOUR_SLACK_TOKEN>"
SLACK_USER_ID = "<YOUR_SLACK_USER_ID>"
SLACK_MESSAGE_URL = "https://slack.com/api/chat.postMessage"
SLACK_STATUS_URL = "https://slack.com/api/users.profile.set"

# 昼休憩中かどうか
is_lunch = False
lock = Lock()


def post_message(channel, message):
    headers = {
        "Authorization": f"Bearer {SLACK_TOKEN}",
        "Content-Type": "application/json; charset=utf-8",
    }

    payload = {
        "channel": channel,
        "text": message,
        "as_user": True,
    }

    send_request(SLACK_MESSAGE_URL, headers, payload)


def change_status(emoji, message):
    headers = {
        "Authorization": f"Bearer {SLACK_TOKEN}",
        "Content-Type": "application/json; charset=utf-8",
        "X-Slack-User": SLACK_USER_ID,
    }

    payload = {
        "profile": {
            "status_emoji": emoji,
            "status_text": message,
        },
    }

    send_request(SLACK_STATUS_URL, headers, payload)


def send_request(url, headers, payload):
    try:
        response = requests.post(
            url,
            headers=headers,
            json=payload,
        )
        response.raise_for_status()
        print("Status: OK")
        print(response.json())
    except requests.exceptions.RequestException as e:
        print(f"RequestException: {e}")
    except Exception as e:
        print(f"Exception: {e}")


def start_lunch():
    change_status(":ohiru:", "お昼休憩中")
    post_message("<部門チャンネル>","お昼休憩に入ります")
    print("お昼休憩に入ります")


def done_lunch():
    change_status(":working:", "仕事中")
    post_message("<部門チャンネル>","お昼休憩終わります")
    print("お昼休憩終わります")


# 接続
def on_connect(client, userdata, flag, rc):
    print("Connected with result code " + str(rc))
    client.subscribe(TOPIC)


# 切断
def on_disconnect(client, userdata, rc):
  if rc != 0:
     print("Unexpected disconnection")


# 受信
def on_message(client, userdata, msg):
    print("topic:[" + msg.topic + "] payload:[" + str(msg.payload) + "]")
    global is_lunch
    if (msg.payload.decode("utf-8") == "lunch_notification"):
        lock.acquire()
        if is_lunch:
            done_lunch()
            is_lunch = False
        else:    
            start_lunch()
            is_lunch = True
        lock.release()


if __name__ == '__main__':
    # プロトコルを v3.1.1 を指定
    client = mqtt.Client(protocol=mqtt.MQTTv311)
    # ハンドラー設定
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect
    client.on_message = on_message
    # access tokenの指定
    client.username_pw_set(BEEBOTTE_ACCESS_TOKEN)
    # 接続
    client.connect(HOST, port=PORT, keepalive=60)
    # 受信ループ
    client.loop_forever()

昼休憩の開始/終了をどう判定するかは色々やりようがあると思いますが、今回は簡単化のためトグル形式にしています。

Slackに投稿を行う処理周りについては冒頭で紹介したこちらのブログ記事を参考にしました。

実行は以下コマンドで実施できます。

python mqtt_sub_beebotte.py

実行確認

用意したNFCタグをデスクの隅に置き、お昼ご飯のためデスクから立ち上がり移動する際にスマホをかざし、戻ってきたタイミングで再度スマホをかざしました。

プログラムのデバッグ表示は以下のようになっており、Slackへの投稿もきちんとできているようでした!

これでPCを操作せずお昼休みを伝えることができます。

今回は自宅のデスクで行いましたが、NFCタグは非常に小さいかつ薄いので、会社に持っていく私物に貼っておけば会社でも同様に運用できます。

最後に

今回は、NFCとMQTTを使って昼休みのSlack報告を楽にしてみました。

参考になりましたら幸いです。

参考文献

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.